using System;
using System.Windows.Forms;
using DevExpress.XtraEditors;
using DevExpress.XtraEditors.Controls;
using CSW.Framework.Common;
using CSW.Framework.UI.Binding;

namespace CSW.Framework.UI.DevEx.Binding
{
    /// <summary>
    /// A wrapper class for binding <see cref="ButtonEdit"/> objects.
    /// </summary>
    public class BindableButtonEdit<T> : IBindableControl
        where T : class
    {
        private readonly ButtonEdit m_ButtonEdit;
        private readonly Func<T> m_GetObject;
        private bool m_IsDisposed;

        /// <summary>
        /// Initializes a new instance of the <see cref="BindableButtonEdit{T}"/> class.
        /// </summary>
        /// <param name="buttonEdit">The <see cref="ButtonEdit"/> control to wrap.</param>
        /// <param name="getObject">The method which is used to select the object. Return <c>null</c> from this method to make no change.</param>
        /// <param name="addClearButton">if set to <c>true</c>, add a clear button.</param>
        public BindableButtonEdit(ButtonEdit buttonEdit, Func<T> getObject, bool addClearButton)
        {
            Guard.ArgumentNotNull(buttonEdit, "buttonEdit");
            Guard.ArgumentNotNull(getObject, "getObject");

            m_ButtonEdit = buttonEdit;
            m_GetObject = getObject;

            Setup(addClearButton);
        }

        private void Setup(bool addClearButton)
        {
            m_ButtonEdit.Validated += ButtonEdit_Validated;
            m_ButtonEdit.ButtonClick += ButtonEdit_ButtonClick;
            m_ButtonEdit.DoubleClick += ButtonEdit_DoubleClick;
            m_ButtonEdit.KeyDown += m_ButtonEdit_KeyDown;
            m_ButtonEdit.Properties.TextEditStyle = TextEditStyles.DisableTextEditor;

            if (addClearButton)
            {
                // Search for butotn
                bool found = false;
                foreach (EditorButton button in m_ButtonEdit.Properties.Buttons)
                {
                    if (button.Kind == ButtonPredefines.Delete)
                    {
                        found = true;
                        break;
                    }
                }

                // If not found, add
                if (!found)
                    m_ButtonEdit.Properties.Buttons.Add(new EditorButton(ButtonPredefines.Delete));

                // Handle click
                m_ButtonEdit.ButtonClick += BaseEditEventHandlers.DeleteButtonClick;
                m_ButtonEdit.KeyDown += BaseEditEventHandlers.DeleteKeyDown;
            }
        }

        private void m_ButtonEdit_KeyDown(object sender, KeyEventArgs e)
        {
            CheckDisposed();

            if (e.Modifiers == Keys.None)
            {
                switch (e.KeyCode)
                {
                    case Keys.F4:
                    case Keys.F2:
                        GetObject();
                        break;
                }
            }
        }

        private void ButtonEdit_ButtonClick(object sender, ButtonPressedEventArgs e)
        {
            CheckDisposed();

            if (e.Button.Kind == ButtonPredefines.Ellipsis)
            {
                GetObject();
            }
        }

        private void ButtonEdit_DoubleClick(object sender, EventArgs e)
        {
            CheckDisposed();

            GetObject();
        }

        private void GetObject()
        {
            CheckDisposed();

            T value = m_GetObject();

            if (value != null)
                Value = value;
        }

        private void ButtonEdit_Validated(object sender, EventArgs e)
        {
            CheckDisposed();

            EventHandler tmpValidated = Validated;
            if (tmpValidated != null)
            {
                tmpValidated(sender, e);
            }
        }

        private void CheckDisposed()
        {
            if (m_IsDisposed)
                throw new ObjectDisposedException(Name, string.Format("'{0}' is disposed.", Name));
        }

        #region IBindableControl Members

        /// <summary>
        /// Occurs when the control has validated.
        /// </summary>
        public event EventHandler Validated;

        /// <summary>
        /// Sends the <see cref="Validated"/> event.
        /// </summary>
        public void SendValidated()
        {
            EventHandler validated = Validated;
            if (validated != null)
                validated(this, EventArgs.Empty);
        }

        /// <summary>
        /// Gets the control this object wraps.
        /// </summary>
        /// <value>The control this object wraps.</value>
        public Control Control
        {
            get { return m_ButtonEdit; }
        }

        /// <summary>
        /// Gets or sets the value of the control.
        /// </summary>
        /// <value>The value of the control.</value>
        public object Value
        {
            get 
            { 
                return m_ButtonEdit.Tag;
            }
            set
            {
                bool valueChanged = (m_ButtonEdit.Tag != value);

                m_ButtonEdit.Tag = value;
                m_ButtonEdit.Text = string.Format("{0}", value);

                if (valueChanged)
                    ButtonEdit_Validated(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Gets the name of the control.
        /// </summary>
        /// <value>The name of the control.</value>
        public string Name
        {
            get { return m_ButtonEdit.Name; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is enabled.
        /// </summary>
        /// <value><c>true</c> if enabled; otherwise, <c>false</c>.</value>
        public bool Enabled
        {
            get { return m_ButtonEdit.Enabled; }
            set { m_ButtonEdit.Enabled = value; }
        }

        /// <summary>
        /// Focuses the control.
        /// </summary>
        public void Focus()
        {
            m_ButtonEdit.Focus();
        }

        /// <summary>
        /// Gets or sets a value indicating whether the control is read only.
        /// </summary>
        /// <value><c>true</c> if read only; otherwise, <c>false</c>.</value>
        public bool ReadOnly
        {
            get { return m_ButtonEdit.Properties.ReadOnly; }
            set
            {
                m_ButtonEdit.Properties.ReadOnly = value;
                m_ButtonEdit.TabStop = !value;

                foreach (EditorButton button in m_ButtonEdit.Properties.Buttons)
                {
                    button.Enabled = !value;
                }
            }
        }

        /// <summary>
        /// Gets the tab index.
        /// </summary>
        /// <value>The tab index.</value>
        public int TabIndex
        {
            get { return m_ButtonEdit.TabIndex; }
        }

        /// <summary>
        /// Gets or sets the maximum string length.
        /// </summary>
        /// <value>The maximum string length.</value>
        public int MaxLength
        {
            get { throw new NotSupportedException("MaxLength is not supported."); }
            set { throw new NotSupportedException("MaxLength is not supported."); }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Validated = null;
            m_ButtonEdit.Validated -= ButtonEdit_Validated;
            m_ButtonEdit.ButtonClick -= ButtonEdit_ButtonClick;
            m_ButtonEdit.DoubleClick -= ButtonEdit_DoubleClick;
            m_ButtonEdit.ButtonClick -= BaseEditEventHandlers.DeleteButtonClick;
            m_ButtonEdit.KeyDown -= BaseEditEventHandlers.DeleteKeyDown;
            m_ButtonEdit.KeyDown -= m_ButtonEdit_KeyDown;
            m_IsDisposed = true;
        }

        #endregion
    }
}